home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / src / add.c < prev    next >
C/C++ Source or Header  |  1992-04-02  |  12KB  |  448 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * Add
  9.  * 
  10.  * Adds a file or directory to the RCS source repository.  For a file,
  11.  * the entry is marked as "needing to be added" in the user's own CVS
  12.  * directory, and really added to the repository when it is committed.
  13.  * For a directory, it is added at the appropriate place in the source
  14.  * repository and a CVS directory is generated within the directory.
  15.  * 
  16.  * The -m option is currently the only supported option.  Some may wish to
  17.  * supply standard "rcs" options here, but I've found that this causes more
  18.  * trouble than anything else.
  19.  * 
  20.  * The user files or directories must already exist.  For a directory, it must
  21.  * not already have a CVS file in it.
  22.  * 
  23.  * An "add" on a file that has been "remove"d but not committed will cause the
  24.  * file to be resurrected.
  25.  */
  26.  
  27. #include "cvs.h"
  28.  
  29. #ifndef lint
  30. static char rcsid[] = "@(#)add.c 1.46 92/04/03";
  31. #endif
  32.  
  33. #if __STDC__
  34. static int add_directory (char *repository, char *dir);
  35. static int build_entry (char *repository, char *user, char *options,
  36.                 char *message, List * entries);
  37. #else
  38. static int add_directory ();
  39. static int build_entry ();
  40. #endif                /* __STDC__ */
  41.  
  42. static char *add_usage[] =
  43. {
  44.     "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
  45.     "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
  46.     "\t-m\tUse \"message\" for the creation log.\n",
  47.     NULL
  48. };
  49.  
  50. int
  51. add (argc, argv)
  52.     int argc;
  53.     char *argv[];
  54. {
  55.     char message[MAXMESGLEN];
  56.     char *user;
  57.     int i;
  58.     char *repository;
  59.     int c;
  60.     int err = 0;
  61.     int added_files = 0;
  62.     char *options = NULL;
  63.     List *entries;
  64.     Vers_TS *vers;
  65.  
  66.     if (argc == 1 || argc == -1)
  67.     usage (add_usage);
  68.  
  69.     /* parse args */
  70.     message[0] = '\0';
  71.     optind = 1;
  72.     while ((c = gnu_getopt (argc, argv, "k:m:")) != -1)
  73.     {
  74.     switch (c)
  75.     {
  76.         case 'k':
  77.         if (options)
  78.             free (options);
  79.         options = RCS_check_kflag (optarg);
  80.         break;
  81.  
  82.         case 'm':
  83.         if (strlen (optarg) >= sizeof (message))
  84.         {
  85.             error (0, 0, "warning: message too long; truncated!");
  86.             (void) strncpy (message, optarg, sizeof (message));
  87.             message[sizeof (message) - 1] = '\0';
  88.         }
  89.         else
  90.             (void) strcpy (message, optarg);
  91.         break;
  92.         case '?':
  93.         default:
  94.         usage (add_usage);
  95.         break;
  96.     }
  97.     }
  98.     argc -= optind;
  99.     argv += optind;
  100.  
  101.     if (argc <= 0)
  102.     usage (add_usage);
  103.  
  104.     /* find the repository associated with our current dir */
  105.     repository = Name_Repository ((char *) NULL, (char *) NULL);
  106.     entries = ParseEntries (0);
  107.  
  108.     /* walk the arg list adding files/dirs */
  109.     for (i = 0; i < argc; i++)
  110.     {
  111.     int begin_err = err;
  112.  
  113.     user = argv[i];
  114.     if (index (user, '/') != NULL)
  115.     {
  116.         error (0, 0,
  117.          "cannot add files with '/' in their name; %s not added", user);
  118.         err++;
  119.         continue;
  120.     }
  121.  
  122.     vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
  123.                user, 0, 0, entries, (List *) NULL);
  124.     if (vers->vn_user == NULL)
  125.     {
  126.         /* No entry available, ts_rcs is invalid */
  127.         if (vers->vn_rcs == NULL)
  128.         {
  129.         /* There is no RCS file either */
  130.         if (vers->ts_user == NULL)
  131.         {
  132.             /* There is no user file either */
  133.             error (0, 0, "nothing known about %s", user);
  134.             err++;
  135.         }
  136.         else if (!isdir (user))
  137.         {
  138.             /*
  139.              * See if a directory exists in the repository with
  140.              * the same name.  If so, blow this request off.
  141.              */
  142.             char dname[PATH_MAX];
  143.             (void) sprintf (dname, "%s/%s", repository, user);
  144.             if (isdir (dname))
  145.             {
  146.             error (0, 0,
  147.                    "cannot add file `%s' since the directory",
  148.                    user);
  149.             error (0, 0, "`%s' already exists in the repository",
  150.                    dname);
  151.             error (1, 0, "illegal filename overlap");
  152.             }
  153.  
  154.             /* There is a user file, so build the entry for it */
  155.             if (build_entry (repository, user, vers->options,
  156.                      message, entries) != 0)
  157.             err++;
  158.             else if (!quiet)
  159.             {
  160.             added_files++;
  161.             error (0, 0, "scheduling file `%s' for addition",
  162.                    user);
  163.             }
  164.         }
  165.         }
  166.         else
  167.         {
  168.  
  169.         /*
  170.          * There is an RCS file already, so somebody else must've
  171.          * added it
  172.          */
  173.         error (0, 0, "%s added independently by second party", user);
  174.         err++;
  175.         }
  176.     }
  177.     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
  178.     {
  179.  
  180.         /*
  181.          * An entry for a new-born file, ts_rcs is dummy, but that is
  182.          * inappropriate here
  183.          */
  184.         error (0, 0, "%s has already been entered", user);
  185.         err++;
  186.     }
  187.     else if (vers->vn_user[0] == '-')
  188.     {
  189.         /* An entry for a removed file, ts_rcs is invalid */
  190.         if (vers->ts_user == NULL)
  191.         {
  192.         /* There is no user file (as it should be) */
  193.         if (vers->vn_rcs == NULL)
  194.         {
  195.  
  196.             /*
  197.              * There is no RCS file, so somebody else must've removed
  198.              * it from under us
  199.              */
  200.             error (0, 0,
  201.                "cannot resurrect %s; RCS file removed by second party", user);
  202.             err++;
  203.         }
  204.         else
  205.         {
  206.  
  207.             /*
  208.              * There is an RCS file, so remove the "-" from the
  209.              * version number and restore the file
  210.              */
  211.             char *tmp = xmalloc (strlen (user) + 50);
  212.  
  213.             (void) strcpy (tmp, vers->vn_user + 1);
  214.             (void) strcpy (vers->vn_user, tmp);
  215.             (void) sprintf (tmp, "Resurrected %s", user);
  216.             Register (entries, user, vers->vn_user, tmp, vers->options,
  217.                   vers->tag, vers->date);
  218.             free (tmp);
  219.  
  220.             /* XXX - bugs here; this really resurrect the head */
  221.             if (update (2, argv + i - 1) == 0)
  222.             {
  223.             error (0, 0, "%s, version %s, resurrected", user,
  224.                    vers->vn_user);
  225.             }
  226.             else
  227.             {
  228.             error (0, 0, "could not resurrect %s", user);
  229.             err++;
  230.             }
  231.         }
  232.         }
  233.         else
  234.         {
  235.         /* The user file shouldn't be there */
  236.         error (0, 0, "%s should be removed and is still there (or is back again)", user);
  237.         err++;
  238.         }
  239.     }
  240.     else
  241.     {
  242.         /* A normal entry, ts_rcs is valid, so it must already be there */
  243.         error (0, 0, "%s already exists, with version number %s", user,
  244.            vers->vn_user);
  245.         err++;
  246.     }
  247.     freevers_ts (&vers);
  248.  
  249.     /* passed all the checks.  Go ahead and add it if its a directory */
  250.     if (begin_err == err && isdir (user))
  251.     {
  252.         err += add_directory (repository, user);
  253.         continue;
  254.     }
  255.     }
  256.     if (added_files)
  257.     error (0, 0, "use 'cvs commit' to add %s permanently",
  258.            (added_files == 1) ? "this file" : "these files");
  259.     dellist (&entries);
  260.     return (err);
  261. }
  262.  
  263. /*
  264.  * The specified user file is really a directory.  So, let's make sure that
  265.  * it is created in the RCS source repository, and that the user's directory
  266.  * is updated to include a CVS directory.
  267.  * 
  268.  * Returns 1 on failure, 0 on success.
  269.  */
  270. static int
  271. add_directory (repository, dir)
  272.     char *repository;
  273.     char *dir;
  274. {
  275.     char cwd[PATH_MAX], rcsdir[PATH_MAX];
  276.     char message[PATH_MAX + 100];
  277.     char *tag, *date;
  278.  
  279.     if (index (dir, '/') != NULL)
  280.     {
  281.     error (0, 0,
  282.            "directory %s not added; must be a direct sub-directory", dir);
  283.     return (1);
  284.     }
  285.     if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0)
  286.     {
  287.     error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM);
  288.     return (1);
  289.     }
  290.  
  291.     /* before we do anything else, see if we have any per-directory tags */
  292.     ParseTag (&tag, &date);
  293.  
  294.     /* now, remember where we were, so we can get back */
  295.     if (getwd (cwd) == NULL)
  296.     {
  297.     error (0, 0, "cannot get working directory: %s", cwd);
  298.     return (1);
  299.     }
  300.     if (chdir (dir) < 0)
  301.     {
  302.     error (0, errno, "cannot chdir to %s", dir);
  303.     return (1);
  304.     }
  305.     if (isfile (CVSADM) || isfile (OCVSADM))
  306.     {
  307.     error (0, 0,
  308.            "%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM);
  309.     goto out;
  310.     }
  311.  
  312.     (void) sprintf (rcsdir, "%s/%s", repository, dir);
  313.     if (isfile (rcsdir) && !isdir (rcsdir))
  314.     {
  315.     error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
  316.     goto out;
  317.     }
  318.  
  319.     /* setup the log message */
  320.     (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
  321.     if (tag)
  322.     {
  323.     (void) strcat (message, "--> Using per-directory sticky tag `");
  324.     (void) strcat (message, tag);
  325.     (void) strcat (message, "'\n");
  326.     }
  327.     if (date)
  328.     {
  329.     (void) strcat (message, "--> Using per-directory sticky date `");
  330.     (void) strcat (message, date);
  331.     (void) strcat (message, "'\n");
  332.     }
  333.  
  334.     if (!isdir (rcsdir))
  335.     {
  336.     mode_t omask;
  337.     char line[MAXLINELEN];
  338.     Node *p;
  339.     List *ulist;
  340.  
  341.     (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
  342.                rcsdir);
  343.     (void) fflush (stdout);
  344.     clearerr (stdin);
  345.     if (fgets (line, sizeof (line), stdin) == NULL ||
  346.         (line[0] != 'y' && line[0] != 'Y'))
  347.     {
  348.         error (0, 0, "directory %s not added", rcsdir);
  349.         goto out;
  350.     }
  351.     omask = umask (2);
  352.     if (mkdir (rcsdir, 0777) < 0)
  353.     {
  354.         error (0, errno, "cannot mkdir %s", rcsdir);
  355.         (void) umask ((int) omask);
  356.         goto out;
  357.     }
  358.     (void) umask ((int) omask);
  359.  
  360.     /*
  361.      * Set up an update list with a single title node for Update_Logfile
  362.      */
  363.     ulist = getlist ();
  364.     p = getnode ();
  365.     p->type = UPDATE;
  366.     p->delproc = update_delproc;
  367.     p->key = xstrdup ("- New directory");
  368.     p->data = (char *) T_TITLE;
  369.     (void) addnode (ulist, p);
  370.     Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
  371.     dellist (&ulist);
  372.     }
  373.  
  374.     Create_Admin (".", rcsdir, tag, date);
  375.     if (tag)
  376.     free (tag);
  377.     if (date)
  378.     free (date);
  379.  
  380.     (void) printf ("%s", message);
  381. out:
  382.     if (chdir (cwd) < 0)
  383.     error (1, errno, "cannot chdir to %s", cwd);
  384.     return (0);
  385. }
  386.  
  387. /*
  388.  * Builds an entry for a new file and sets up "CVS/file",[pt] by
  389.  * interrogating the user.  Returns non-zero on error.
  390.  */
  391. static int
  392. build_entry (repository, user, options, message, entries)
  393.     char *repository;
  394.     char *user;
  395.     char *options;
  396.     char *message;
  397.     List *entries;
  398. {
  399.     char fname[PATH_MAX];
  400.     char line[MAXLINELEN];
  401.     FILE *fp;
  402.  
  403.     /*
  404.      * There may be an old file with the same name in the Attic! This is,
  405.      * perhaps, an awkward place to check for this, but other places are
  406.      * equally awkward.
  407.      */
  408.     (void) sprintf (fname, "%s/%s/%s%s", repository, CVSATTIC, user, RCSEXT);
  409.     if (isreadable (fname))
  410.     {
  411.     error (0, 0, "there is an old file %s already in %s/%s", user,
  412.            repository, CVSATTIC);
  413.     return (1);
  414.     }
  415.  
  416.     if (noexec)
  417.     return (0);
  418.  
  419.     /*
  420.      * The options for the "add" command are store in the file CVS/user,p
  421.      */
  422.     (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
  423.     fp = open_file (fname, "w+");
  424.     if (fclose (fp) == EOF)
  425.     error(1, errno, "cannot close %s", fname);
  426.  
  427.     /*
  428.      * And the requested log is read directly from the user and stored in the
  429.      * file user,t.  If the "message" argument is set, use it as the
  430.      * initial creation log (which typically describes the file).
  431.      */
  432.     (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
  433.     fp = open_file (fname, "w+");
  434.     if (*message && fputs (message, fp) == EOF)
  435.         error (1, errno, "cannot write to %s", fname);
  436.     if (fclose(fp) == EOF)
  437.         error(1, errno, "cannot close %s", fname);
  438.  
  439.     /*
  440.      * Create the entry now, since this allows the user to interrupt us above
  441.      * without needing to clean anything up (well, we could clean up the ,p
  442.      * and ,t files, but who cares).
  443.      */
  444.     (void) sprintf (line, "Initial %s", user);
  445.     Register (entries, user, "0", line, options, (char *) 0, (char *) 0);
  446.     return (0);
  447. }
  448.